#!/usr/bin/python
# -*- coding: utf-8; -*-

import os
import sys
import re
import urllib
import appuifw2
import e32
import key_codes
import socket
import graphics

UID=u"e3e34da4"
VERSION='2.5'

u = lambda s: s.decode('utf-8')
s = lambda s: s.encode('utf-8')

ParaChar = u"\u2029"
# 5M max size for multimedia content using normal POST method
MAX_MEDIA_SIZE = 5 * 1024 * 1024
TumblrApiURL = 'http://www.tumblr.com/api/'
TumblrWriteURL = TumblrApiURL + 'write'
TumblrAuthURL = TumblrApiURL + 'authenticate'
APO = None   # default access point
progpath = os.path.split(sys.argv[0])[0]
datadir = progpath[:2] + '\\System\\Data\\'

def stars(s):
    '''replace each character in s with asterisk
    '''
    return '*' * len(s)

def errorMessage(msg):
    appuifw2.query(msg, 'query', ok=u'', cancel=u('Close'))

def selectAccessPoint():
    global APO
    if APO is None:
        apid = socket.select_access_point()
        e32.ao_yield()
        if apid is not None:
            APO = socket.access_point(apid)
            try:
                socket.set_default_access_point(APO)
                APO.start()
            except:
                APO = None
                socket.set_default_access_point(APO)
                errorMessage(u("Cannot use selected access point!"))

def fileBrowser(label, dirname='', ext=None):
    isdir = lambda fname: os.path.isdir(os.path.join(dirname, fname))
    isfile = lambda fname: not os.path.isdir(os.path.join(dirname, fname))
    markdir = lambda fname: fname + '/'
    matched = lambda fname: isfile(fname) and (not ext or os.path.splitext(fname)[1].lower() in ext)
    def chkdir(d): # os.path uses '/' as a dir separator but os.path.join
        d = s(d)   # adds no '/' between drive name and file path
        if not (d.endswith('/') or d.endswith('\\')):
            return d + '/' # content handler return an error when there is
        else:              # no separator after drive name
            return d
    while True:
        if not dirname:
            items = map(chkdir, e32.drive_list())
        else:
            lst = os.listdir(dirname)
            dirs = map(markdir, filter(isdir, lst))
            dirs.sort()
            files = filter(matched, lst)
            files.sort()
            items = ['..'] + dirs + files
        ans = appuifw2.popup_menu(map(u, items), u(label))
        if ans is None: return None
        fname = items[ans]
        if fname == '..':
            return fname
        fname = os.path.join(dirname, fname)
        if os.path.isdir(fname):
            ans = fileBrowser(fname, fname, ext)
            if ans != '..': return ans
        else:
            return fname

class HTTPError(Exception):
    pass

class TumblrURLopener(urllib.FancyURLopener):
    '''Catch HTTP response codes from Tumblr API
    '''
    def http_error_default(self, url, fp, errcode, errmsg, headers, data=None):
        if errcode == 201:
            return fp
        if errcode == 403:
            errmsg = 'Access forbidden! Check your email and password.'
        elif errcode == 400:
            errmsg = fp.read()
        else:
            errmsg = '%s: %s' % (errcode, errmsg)
        raise HTTPError(errmsg)

urllib._urlopener = TumblrURLopener()

class TumblrGroups(object):
    '''Interface to Tumblr groups
    '''
    def __init__(self, email='', password='', default=None):
        self.setCredentials(email, password)
        self._default_group = default
        self._groups = None

    def setCredentials(self, email, password):
        self.email = email
        self.password = password

    def loadGroups(self):
        if not (self.email and self.password):
            errorMessage(u('Set Email and Password to access Tumblr!'))
            return
        selectAccessPoint()
        data = dict()
        data['email'] = self.email
        data['password'] = self.password
        d = urllib.urlencode(data)
        try:
            a = urllib.urlopen(TumblrAuthURL, d)
        except Exception, detail:
            errorMessage(u(str(detail)))
            return
        self._groups = dict()
        xml = a.read()
        for item in xml.split('<'):
            item = item.strip()
            while item and item[-1] in ' />':
                item = item[:-1]
            if not item: continue
            if item[0] in '/?': continue
            tag, attrs = item.split(' ', 1)
            if tag == 'tumblelog':
                gid = ''
                gtitle = ''
                for i in re.finditer('([^ ]+)="([^"]+)"', attrs):
                    k = i.group(1)
                    v = i.group(2)
                    if k == 'name':
                        gid = v + '.tumblr.com'
                    elif k == 'private-id':
                        gid = v
                    elif k == 'title':
                        gtitle = v
                self._groups[gid] = gtitle
        a.close()

    def selectGroup(self, refresh=False):
        if self._groups is None or refresh:
            self.loadGroups()
            if self._groups is None:
                return None
        gnames = self._groups.values()
        gnames.sort()
        ans = appuifw2.popup_menu(map(u, gnames), u('Select Group'))
        if ans is not None:
            return self.getURL(gnames[ans])
        return None

    def selectDefaultGroup(self, refresh=False):
        self._default_group = self.selectGroup(refresh)

    def getDefaultGroup(self):
        return self._default_group

    def setDefaultGroup(self, name):
        self._default_group = name

    defaultGroup = property(getDefaultGroup, setDefaultGroup)

    def getGroups(self):
        return self._groups.copy()

    def setGroups(self, d):
        self._groups = d.copy()

    groups = property(getGroups, setGroups)

    def getURL(self, gname):
        for k, v in self._groups.items():
            if v == gname:
                return k
        return None

    def getName(self, url):
        try:
            return self._groups[url]
        except:
            return ''

    def __str__(self):
        if self._groups is None:
            return ''
        return '\t'.join([ '%s %s' % (url, name) for url, name in self._groups.items() ])

    def parseGroups(self, s):
        try:
            self._groups = dict()
            for item in s.split('\t'):
                url, name = item.split(' ', 1)
                self._groups[url] = name
        except ValueError:
            self._groups = None

class MenuView(appuifw2.View):
    '''View with menu
    '''
    def __init__(self, title, menu, callbacks, menu_key=(u"Select", None), exit_key=(u"Back", None)):
        appuifw2.View.__init__(self)
        self.title = title
        self.setMenu(menu, tuple(callbacks))
        self.menu_key_text = menu_key[0]
        self.exit_key_text = exit_key[0]
        if menu_key[1] is not None:
            self.menu_key_handler = menu_key[1]
        else:
            self.menu_key_handler = self.menuEvent
        if exit_key[1] is not None:
            self.exit_key_handler = exit_key[1]
        else:
            exit_key_handler = self.close

    def menuEvent(self):
        selected = self.my_menu.current()
        self.callbacks[selected]()

    def setMenu(self, menu, callbacks):
        self.menu_items = list(menu)
        self.callbacks = list(callbacks)
        self.my_menu = appuifw2.Listbox(self.menu_items, self.menuEvent)
        self.body = self.my_menu

    def resetMenu(self):
        selected = self.my_menu.current()
        self.my_menu.set_list(self.menu_items, selected)
        self.body = self.my_menu

    def updateMenuItem(self, item):
        '''item must be tuple suitable for Listbox item
        '''
        s = item[0]
        for i in range(len(self.menu_items)):
            if self.menu_items[i][0] == s:
                self.menu_items[i] = item
                self.resetMenu()
                return

class EditView(appuifw2.View):
    '''Text editor with Markdown capabilities
    '''
    def __init__(self, caption, text, font='normal', callback=None):
        appuifw2.View.__init__(self)
        self.title = u('Edit %s' % (caption.capitalize()))
        self.edit_callback = callback
        self.editor = appuifw2.Text(skinned=True, edit_callback=self.changeEvent)
        if font:
            self.editor.font = font
#        self.editor.style = appuifw2.STYLE_BOLD
        self.editor.set(u(text))
        self.body = self.editor
        self.menu = [
            (u('Bold'),
             lambda : self.insertText(u'**', u'**')),
            (u('Italic'), 
             lambda : self.insertText(u'_', u'_')),
            (u('Code'), 
             lambda : self.insertText(u'`', u'`')),
            (u('Link'), 
             lambda : self.insertText(u'[', u']()')),
            (u('List item'), 
             lambda : self.insertText(u'* ')),
            (u('Blockquote'), 
             lambda : self.insertText(u'> ')),
            (u('Preformated'), 
             lambda : self.insertText(u'    ')),
            (u('Paragraph'),
             lambda : self.insertText(ParaChar+ParaChar)),
            (u('More Tag'),
             lambda : self.insertText(u'<!-- more -->' + ParaChar)),
            (u('Character'),
             ((u('--'), lambda : self.insertText(u'--')),
              (u('"'), lambda : self.insertText(u'"')),
              (u('()'), lambda : self.insertText(u'(', u')')),
              (u('[]'), lambda : self.insertText(u'[', u']')),
              ))
            ]
        self.editor.bind(key_codes.EKeySelect, self.selectKeyPressed)
        self.editor.bind(key_codes.EKeyYes, self.cxapeligu)
        self.menu_key_text = u('Insert')
        self.exit_key_text = u('Back')
        self.exit_key_handler = self.exitEvent

    def cxapeligu(self):
        '''Redonas la saman tekston kun ĉapeligitajn literojn el x-skribo
        '''
        unikoda = ('Ĉ','ĉ','Ĝ','ĝ','Ĥ','ĥ','Ĵ','ĵ','Ŝ','ŝ','Ŭ','ŭ')
        xskriba = ('Cx','cx','Gx','gx','Hx','hx','Jx','jx','Sx','sx','Ux','ux')
        txt = self.body.get()
        for i in range(len(unikoda)):
            txt = txt.replace(u(xskriba[i]), u(unikoda[i]))
        self.body.set(txt)

    def changeEvent(self, pos, num):
        if self.edit_callback is not None:
            self.edit_callback()

    def exitEvent(self):
        self.editor.set_pos(0)
        self.close()

    def getText(self):
        return s(self.body.get().replace(ParaChar, u'\r\n'))

    def insertText(self, before, after=u''):
        '''Insert text around selection if any
        before and after must be unicode strings
        '''
        (pos, anchor, text) = self.editor.get_selection()
        text = u('%s%s%s') % (before, text, after)
        if pos > anchor:
            pos, anchor = anchor, pos
        self.editor.delete(pos, anchor-pos)
        self.editor.set_pos(pos)
        self.editor.add(text)

    def selectKeyPressed(self):
        def getCurrentLine():
            pos = self.editor.get_pos()
            txt = self.editor.get()[:pos]
            # look for the start of line
            i = pos-1
            while i >= 0 and txt[i] != ParaChar:
                i -= 1
            return txt[i+1:]
        line = getCurrentLine()
        indentchars = u''
        mo = re.match(u'^( *(> *|[-+*] +|[0-9]\. +| +)+)', line)
        if mo is not None:
                indentchars += mo.group(1)
        self.editor.add(ParaChar+indentchars)

class PhotoView(appuifw2.View):
    '''Show photo and allow select it from disks
    '''
    def __init__(self, image_name=None, callback=None):
        appuifw2.View.__init__(self)
        self.title = u('Photo')
        self.edit_callback = callback
        self.canvas = appuifw2.Canvas(redraw_callback=self.draw)
        self.canvas.clear(0)
        self.image_name = image_name
        self.image = None
        self.body = self.canvas
        self.menu_key_text = u('Change')
        self.menu_key_handler = self.selectImage
        self.exit_key_text = u('Back')
        self.exit_key_handler = self.exitEvent
        if not image_name:
            self.selectImage()
        else:
            self.loadImage()

    def loadImage(self):
        if not self.image_name:
            return
        if not os.path.exists(self.image_name):
            errorMessage(u('Selected file does not exist!'))
            self.image_name = ''
            return
        fname = self.image_name.replace('/', '\\')
        prop = graphics.Image.inspect(fname)
        image = graphics.Image.new(prop['size'])
        image.load(fname)
        self.image = image.resize(self.canvas.size, keepaspect=1)

    def draw(self, rect=None):
        self.canvas.clear(0)
        if self.image:
            srcx = (self.canvas.size[0] - self.image.size[0]) / 2
            srcy = (self.canvas.size[1] - self.image.size[1]) / 2
            self.canvas.blit(self.image, target=(srcx, srcy))

    def selectImage(self):
        fname = fileBrowser('Select File', ext=['.jpg', '.png'])
        if fname is None:
            return
        try:
            fdata = open(fname, 'rb').read()
            if len(fdata) > MAX_MEDIA_SIZE:
                raise MemoryError
        except MemoryError:
            errorMessage(u('File exceedes maximum size of 5M'))
            return
        self.image_name = fname
        self.loadImage()
        self.draw()
            
    def getFilename(self):
        return self.image_name
    
    def exitEvent(self):
        self.close()

class WaitView(appuifw2.View):
    def __init__(self, caption, text1=None, text2=None):
        appuifw2.View.__init__(self)
        self.title = caption
        self.body = appuifw2.Text_display(skinned=True)
        if text1:
            self.body.font = "title"
            self.body.add(u('\n'))
            self.body.add(text1)
            self.body.add(u('\n'))
        if text2:
            self.body.font = "normal"
            self.body.add(u('\n'))
            self.body.add(text2)
        self.menu_key_text = u''
        self.exit_key_text = u('Cancel')

    def setText(self, txt):
        self.body.font = "title"
        self.body.set(txt)

class Post(object):
    '''Common Post object
    type may be: regular, photo, quote, link
    '''
    FIELDS = {
        'regular': ('title', 'body'),
        'quote':   ('source', 'quote'),
        'photo':   ('FILENAME', 'caption'),
        'link':    ('name', 'url')
        }
    STATES = ['published', 'draft', 'queue'] # we don't support submissions
    def __init__(self, posttype, email, password, groups, twitter, font='normal'):
        self.fname = datadir + 'stumblr.txt'
        self.email = email
        self.password = password
        self.font = font
        self.data = dict()
        self.data["email"] = self.email
        self.data["password"] = self.password
        self.data["generator"] = "stumblr"
        self.data["format"] = "markdown"
        self.data['type'] = posttype
        self.data['private'] = 0
## Tumblr API:
## state (optional) - One of the following values:
##     published (default)
##     draft - Save in the tumblelog's Drafts folder for later publishing.
##     submission - Add to the tumblelog's Messages folder for consideration.
##     queue - Add to the tumblelog's queue for automatic publishing in a
##        few minutes or hours. To publish at a specific time in the
##        future instead, specify an additional publish-on parameter
##        with the date expression in the tumblelog's local time
##        (e.g. publish-on=2010-01-01T13:34:00).
##        If the date format cannot be understood, a 401 error will be
##        returned and the post will not be created.
        
        self.data['state'] = 'published'

## send-to-twitter (optional, ignored on edits) - One of the following
##   values, if the tumblelog has Twitter integration enabled:
##  no (default) - Do not send this post to Twitter.
##  auto - Send to Twitter with an automatically generated summary of the post.
##  (any other value) - A custom message to send to Twitter for this post.

        self.data['send-to-twitter'] = twitter
        
        self.groups = groups
        group = self.groups.defaultGroup
        if group:
            self.data['group'] = group
        for fld in self.FIELDS[self.type]:
            self.data[fld] = ''
        self.data['tags'] = ''
        self.fload()

    def fsave(self):
        fd = open(self.fname, 'wb')
        fd.write('Version %s\n' % VERSION)
        fd.write('%s\n' % self.data['type'])
        fd.write('%s\n' % self.data['private'])
        fd.write('%s\n' % self.data['tags'])
        if self.data.has_key('group'):
            fd.write('%s\n' % self.data['group'])
        else:
            fd.write('\n')
        fd.write('%s\n' % self.data['state'])
        fd.write('%s\n' % self.data['send-to-twitter'])
        ftitle, fdata = self.FIELDS[self.type]
        fd.write('%s\n' % self.data[ftitle])
        fd.write(self.data[fdata])
        fd.close()

    def fclear(self):
        os.unlink(self.fname)

    def fload(self):
        if not os.path.exists(self.fname):
            return
        fd = open(self.fname, 'rb')
        version = fd.readline().strip()
        if version.startswith('Version'):
            version = version.split(' ', 1)[1][0]
            postType = fd.readline().strip()
        else:
            postType = version
            version = None
        if postType != self.type:
            fd.close()
            return
        try:
            self.data['private'] = int(fd.readline().strip())
        except:
            fd.close()
            self.fclear()
            return
        self.data['tags'] = fd.readline().strip()
        group = fd.readline().strip()
        if group:
            self.data['group'] = group
        else:
            try: del self.data['group']
            except: pass
        if version >= '2':
            self.data['state'] = fd.readline().strip()
            self.data['send-to-twitter'] = fd.readline().strip()
        ftitle, fdata = self.FIELDS[self.type]
        self.data[ftitle] = fd.readline().strip()
        self.data[fdata] = fd.read()
        fd.close()
        
    def getType(self): return self.data['type']
    
    type = property(getType)

    def changePrivate(self):
        if self.data.has_key('private') and self.data['private'] == 1:
            self.data['private'] = 0
        else:
            self.data['private'] = 1
        self.updateViewMenu(u('Private'), self.private)
        self.fsave()

    def getPrivate(self):
        if self.data.has_key('private') and self.data['private'] == 1:
            return u('Yes')
        else:
            return u('No')

    private = property(getPrivate)

    def setTags(self, tags):
        self.data['tags'] = tags
        self.fsave()

    def getTags(self):
        return self.data['tags']

    tags = property(getTags, setTags)

    def setState(self, state):
        '''state may be "published", "draft", "submission" or "queue"
        '''
        self.data['state'] = state
        self.fsave()

    def getState(self):
        return self.data['state']

    state = property(getState, setState)

    def changeState(self):
        cur = self.state
        next = (self.STATES.index(cur) + 1) % len(self.STATES)
        self.setState(self.STATES[next])
        self.updateViewMenu(u('State'), self.state.capitalize())
        self.fsave()

    def setTwitterStatus(self, status):
        '''status may be "no", "auto" or any text
        '''
        self.data['send-to-twitter'] = status

    def getTwitterStatus(self):
        return self.data['send-to-twitter']

    twitterStatus = property(getTwitterStatus, setTwitterStatus)

    def changeTwitterStatus(self):
        options = [u('No'), u('Auto'), u('Custom')]
        ans = appuifw2.popup_menu(options, u('Send to Twitter'))
        if ans == 0:
            self.twitterStatus = 'no'
        elif ans == 1:
            self.twitterStatus = 'auto'
        else:
            if self.twitterStatus in ('no', 'auto'):
                txt = ''
            else:
                txt = self.twitterStatus
            ans = appuifw2.query(u('Tweet'), 'text', u(txt))
            if ans is not None:
                self.twitterStatus = s(ans)
        self.updateViewMenu(u('Twitter'), u(self.twitterStatus))
        self.fsave()

    def updateViewMenu(self, caption, text):
        appuifw2.app.view.updateMenuItem((caption, text.replace(u'\r\n', u' ')))

    def editTitle(self):
        if self.type == 'quote':
            field = 'source'
        elif self.type == 'photo':
            field = 'caption'
        elif self.type == 'link':
            field = 'name'
        else:
            field = 'title'
        respond = appuifw2.query(u(field.capitalize()), 'text', u(self.data[field]))
        if respond:
            self.data[field] = s(respond)
            self.updateViewMenu(u(field.capitalize()), respond)
        else:
            self.data[field] = ''
            self.updateViewMenu(u(field.capitalize()), u'')
        self.fsave()

    def editTags(self):
        respond = appuifw2.query(u('Tags (comma separated)'), 'text', u(self.data['tags']))
        if respond:
            self.data['tags'] = s(respond)
            self.updateViewMenu(u('Tags'), respond)
        else:
            self.data['tags'] = ''
            self.updateViewMenu(u('Tags'), u'')
        self.fsave()

    def editText(self):
        def autosave():
            self.data[field] = editView.getText()
            self.fsave()
        if self.type == 'quote':
            field = 'quote'
            menu = u'Quote'
        elif self.type == 'regular':
            field = 'body'
            menu = u'Text'
        elif self.type == 'photo':
            field = 'caption'
            menu = u'Caption'
        else:
            raise Exception('No text editable for this post type')
        editView = EditView(field, self.data[field], self.font, autosave)
        appuifw2.app.view = editView
        editView.wait_for_close()
        autosave()
        self.updateViewMenu(menu, u(self.data[field]))

    def editURL(self):
        url = self.data['url'].strip()
        if not url:
            url = u('http://')
        respond = appuifw2.query(u('URL'), 'text', url)
        if respond:
            self.data['url'] = s(respond)
            self.updateViewMenu(u('URL'), respond)
        else:
            self.data['url'] = ''
            self.updateViewMenu(u('URL'), u'')
        self.fsave()

    def editPhoto(self):
        def autosave():
            self.data['FILENAME'] = photoView.getFilename()
            self.fsave()
        photoView = PhotoView(self.data['FILENAME'], autosave)
        appuifw2.app.view = photoView
        photoView.wait_for_close()
        autosave()
        self.updateViewMenu(u('Photo'), u(os.path.split(self.data['FILENAME'])[1]))
        self.fsave()

    def changeGroup(self):
        group = self.groups.selectGroup()
        if group is None:
            return
        self.data['group'] = group
        self.updateViewMenu(u('Group'), u(self.groups.getName(group)))
        self.fsave()
        
    def getGroup(self):
        if self.data.has_key('group'):
            return self.data['group']
        else:
            return ''

    group = property(getGroup)

    def send(self):
        data = self.data.copy()
        if self.type == 'photo':
            try:
                data['data'] = open(data['FILENAME'], 'rb').read()
                del data['FILENAME']
            except IOError:
                errorMessage(u('Cannot read file %s!' % data['FILENAME']))
                return False
        if self.type == 'regular' and len(data['title'] + data['body']) == 0:
            errorMessage(u('Text posts require either a body field or title field'))
            return False
        if self.type == 'quote' and len(data['quote']) == 0:
            errorMessage(u('Quote posts require quote text to send!'))
            return False
        if self.type == 'photo' and len(data['data']) == 0:
            errorMessage(u('Photo posts require image data to send!'))
            return False
        if self.type == 'link' and len(data['url']) == 0:
            errorMessage(u('Link posts require URL to send!'))
            return False
        w = WaitView(u('Sending...'), u('Sending your post...'), u('Please, wait...'))
        appuifw2.app.view = w
        selectAccessPoint()
        if APO is None:
            w.close()
            return False
        if not (data.has_key('group') and data['group']):
            data['group'] = self.groups.defaultGroup
        if data.has_key('group') and not data['group']:
            del data['group']
        edata = urllib.urlencode(data)
        try:
            a = urllib.urlopen(TumblrWriteURL, edata)
            appuifw2.note(u('The post has been sent!'), 'conf')
            w.close()
            self.fclear()
            return True
        except Exception, detail:
            sdetail = str(detail)
            if sdetail.find('Errno socket error') >= 0:
                sdetail = 'Internal error occured.\nReboot your phone, please!'
            errorMessage(u(sdetail))
            w.close()
            return False

class STumblr(object):
    '''Main app
    '''
    def __init__(self):
        self.title = u('stumblr %s' % (VERSION))
        self.configFile = datadir + 'stumblr.cfg'
        if not self.loadSettings():
            self.Settings()
        self.mainView = MenuView(self.title,
                                 [u("Post Text"),
                                  u("Post Photo"),
                                  u("Post Quote"),
                                  u("Post Link"),
                                  u("Settings"),
                                  u("About"),
                                  u("Exit")],
                                 [self.postText,
                                  self.postPhoto,
                                  self.postQuote,
                                  self.postLink,
                                  self.Settings,
                                  self.aboutBox,
                                  self.exitEvent],
                                 exit_key=(u("Exit"), self.exitEvent))

    def loadSettings(self):
        self.email = ''
        self.password = ''
        self.groups = TumblrGroups(self.email, self.password)
        self.font = 'normal'
        self.autotags = {'regular': '', 'quote': '', 'photo': '', 'link': ''}
        self.sendToTwitter = 'auto'
        sGroups = None
        sDefault = None
        try:
            fd = open(self.configFile, 'r')
        except IOError:
            return False
        for line in fd.readlines():
            (k, v) = line.strip().split('=', 1)
            if k == 'Email':
                self.email = v
            elif k == 'Password':
                self.password = v
            elif k == 'Font':
                self.font = v
            elif k == 'Group':
                if v:
                    sDefault = v
            elif k == 'Groups':
                sGroups = v
            elif k == 'Twitter':
                self.sendToTwitter = v
            elif k in self.autotags.keys():
                self.autotags[k] = v
        fd.close()
        self.groups.setCredentials(self.email, self.password)
        if sGroups is not None:
            self.groups.parseGroups(sGroups)
        if sDefault is not None:
            self.groups.defaultGroup = sDefault
        return True

    def saveSettings(self):
        fd = open(self.configFile, 'w')
        fd.write('Email=%s\n' % (self.email))
        fd.write('Password=%s\n' % (self.password))
        fd.write('Font=%s\n' % (self.font))
        if self.groups.defaultGroup is None:
            fd.write('Group=\n')
        else:
            fd.write('Group=%s\n' % (self.groups.defaultGroup))
        fd.write('Groups=%s\n' % (self.groups))
        for k, v in self.autotags.items():
            fd.write('%s=%s\n' % (k, v))
        fd.write('Twitter=%s\n' % self.sendToTwitter)
        fd.close()

    def checkCredentials(self):
        if not self.email or not self.password:
            errorMessage(u('Email or password not set!'))
            return False
        return True

    def sendPost(self, post, view):
        if not post.group and len(self.groups.groups) > 0:
            post.changeGroup()
        if post.send():
            view.close()

    def postAny(self, postType):
        '''Post any type
        '''
        if not self.checkCredentials():
            return
        post = Post(postType, self.email, self.password, self.groups, self.sendToTwitter, self.font)
        if not post.tags:
            post.tags = self.autotags[postType]
        if postType == 'regular':
            postTitle = u('Post Text')
        else:
            postTitle = u('Post ' + postType.capitalize())
        if postType == 'regular':
            menu = [(u('Title'), u(post.data['title'])),
                    (u('Text'), u(post.data['body']))]
            callbacks = [post.editTitle,
                         post.editText]
        elif postType == 'quote':
            menu = [(u('Source'), u(post.data['source'])),
                    (u('Quote'), u(post.data['quote']))]
            callbacks = [post.editTitle,
                         post.editText]
        elif postType == 'photo':
            if post.data['FILENAME']:
                txt = os.path.split(post.data['FILENAME'])[1]
            else:
                txt = ''
            menu = [(u('Photo'), u(txt)),
                    (u('Caption'), u(post.data['caption']))]
            callbacks = [post.editPhoto,
                         post.editText]
        elif postType == 'link':
            menu = [(u('Name'), u(post.data['name'])),
                    (u('URL'), u(post.data['url']))]
            callbacks = [post.editTitle,
                         post.editURL]
        menu += [(u('Tags'), u(post.tags)),
                 (u('Private'), post.private),
                 (u('State'), post.state.capitalize()),
                 (u('Group'), u(self.groups.getName(post.group))),
                 (u('Twitter'), u(post.twitterStatus))]
        callbacks += [post.editTags,
                      post.changePrivate,
                      post.changeState,
                      lambda : post.changeGroup(),
                      post.changeTwitterStatus]
        menuView = MenuView(postTitle, menu, callbacks,
                            menu_key=(u'Post', lambda : self.sendPost(post, menuView)))
        appuifw2.app.view = menuView
        menuView.wait_for_close()
            
    def postText(self):
        self.postAny('regular')

    def postQuote(self):
        self.postAny('quote')

    def postPhoto(self):
        self.postAny('photo')

    def postLink(self):
        self.postAny('link')

    def changeEmail(self):
        respond = appuifw2.query(u('Email address'), 'text', u(self.email))
        if respond:
            self.email = s(respond)
            appuifw2.app.view.updateMenuItem((u('Email'), respond))
        else:
            self.email = ''
            appuifw2.app.view.updateMenuItem((u('Email'), u''))
        self.saveSettings()

    def changePassword(self):
        respond = appuifw2.query(u('Password'), 'code')
        if respond:
            self.password = s(respond)
            appuifw2.app.view.updateMenuItem((u('Password'), u(stars(self.password))))
        else:
            self.password = ''
            appuifw2.app.view.updateMenuItem((u('Password'), u''))
        self.saveSettings()

    def changeFont(self):
        fonts = ('normal', 'annotation', 'title', 'legend', 'symbol', 'dense')
        respond = appuifw2.popup_menu(map(u, fonts), u('Select font'))
        if respond is not None:
            self.font = fonts[respond]
            appuifw2.app.view.updateMenuItem((u('Font'), u(self.font)))

    def changeAutotags(self, postType):
        respond = appuifw2.query(u('Tags (comma separated)'), 'text', u(self.autotags[postType]))
        if respond is None:
            respond = u''
        self.autotags[postType] = s(respond)
        if postType == 'regular':
            postType = 'text'
        appuifw2.app.view.updateMenuItem((u('%s tags' % postType.capitalize()), respond))

    def changeGroup(self):
        w = WaitView(u('Fetching...'), u('Fetching groups...'), u('Please, wait...'))
        appuifw2.app.view = w
        self.groups.setCredentials(self.email, self.password)
        self.groups.selectDefaultGroup(True)
        group = self.groups.defaultGroup
        w.close()
        if group is not None:
            appuifw2.app.view.updateMenuItem((u('Default group'), u(self.groups.getName(group))))
        else:
            appuifw2.app.view.updateMenuItem((u('Default group'), u''))

    def changeTwitter(self):
        if self.sendToTwitter == 'auto':
            self.sendToTwitter = 'no'
        else:
            self.sendToTwitter = 'auto'
        appuifw2.app.view.updateMenuItem((u('Send to Twitter'), self.sendToTwitter.capitalize()))

    def Settings(self):
        menuView = MenuView(u('Settings'),
                            [(u('Email'), u(self.email)),
                             (u('Password'), u(stars(self.password))),
                             (u('Font'), u(self.font)),
                             (u('Default group'), u(self.groups.getName(self.groups.defaultGroup))),
                             (u('Send to Twitter'), u(self.sendToTwitter.capitalize())),
                             (u('Text tags'), u(self.autotags['regular'])),
                             (u('Photo tags'), u(self.autotags['photo'])),
                             (u('Quote tags'), u(self.autotags['quote'])),
                             (u('Link tags'), u(self.autotags['link']))],
                            (self.changeEmail,
                             self.changePassword,
                             self.changeFont,
                             self.changeGroup,
                             self.changeTwitter,
                             lambda : self.changeAutotags('regular'),
                             lambda : self.changeAutotags('photo'),
                             lambda : self.changeAutotags('quote'),
                             lambda : self.changeAutotags('link')))
        appuifw2.app.view = menuView
        menuView.wait_for_close()

    def aboutBox(self):
        w = WaitView(u('About'),
                     u('Stumblr Ver.%s' % (VERSION)),
                     u('S60 Tumblr client\n\n(C) D.Brechalov, 2010'))
        appuifw2.app.view = w
        w.wait_for_close()

    def exitEvent(self):
        self.saveSettings()
        self.mainView.close()
        if appuifw2.app.uid() == UID:
            appuifw2.app.set_exit() # running as app

    def run(self):
        '''Set up and show main screen of the application
        '''
        appuifw2.app.title = self.title
        appuifw2.app.view = self.mainView
        self.mainView.wait_for_close()

if __name__ == '__main__':
    app = STumblr()
    app.run()

